package com.tian.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import com.tian.client.ChargeUserClient;
import com.tian.client.CouponConditionClient;
import com.tian.client.UserCouponClient;
import com.tian.common.CommonResult;
import com.tian.dto.WxPrePayReqDto;
import com.tian.dto.coupon.condition.CouponConditionRespDto;
import com.tian.dto.user.UserCouponRespDto;
import com.tian.dto.user.UserCouponUpdateReqDto;
import com.tian.entity.ChargePayOrder;
import com.tian.enums.CouponConditionType;
import com.tian.enums.OrderTypeEnum;
import com.tian.enums.UserUpdatePointEnum;
import com.tian.enums.WxPayStatusEnum;
import com.tian.mapper.ChargePayOrderMapper;
import com.tian.message.MessageReqIdPrefixConstant;
import com.tian.message.UserPointMessage;
import com.tian.producer.UserPointProducer;
import com.tian.service.ChargePayOrderService;
import com.tian.util.DateUtils;
import com.tian.util.StringUtil;
import com.tian.util.XmlUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.UUID;

/**
 * {@code @description:} 支付单
 *
 * @author tianwc 公众号：Java后端技术全栈
 * 在线刷题 1200+java面试题和1000+篇技术文章：<a href="https://woaijava.cc/">博客地址</a>
 * {@code @date:} 2024/2/20 21:25
 * {@code @version:} 1.0
 */
@Slf4j
@Service
public class ChargePayOrderServiceImpl implements ChargePayOrderService {
    @Resource
    private ChargePayOrderMapper chargePayOrderMapper;
    @Resource
    private UserCouponClient userCouponClient;
    @Resource
    private ChargeUserClient chargeUserClient;
    @Resource
    private UserPointProducer userPointProducer;
    @Resource
    private CouponConditionClient couponConditionClient;

    /**
     * 支付时，通过使用优惠券id 计算出应该减去多少
     */

    @Override
    public CommonResult<WxPayMpOrderResult> prePay(WxPrePayReqDto wxPrePayReqDto) {
        ChargePayOrder chargePayOrder = new ChargePayOrder();
        chargePayOrder.setUserId(wxPrePayReqDto.getUserId());
        chargePayOrder.setOrderNo(wxPrePayReqDto.getOrderNo());
        chargePayOrder.setOrderType(wxPrePayReqDto.getChargeRecordId() == null ? OrderTypeEnum.CHARGE.getBusiness()
                : OrderTypeEnum.PAY_CHARGER.getBusiness());
        chargePayOrder.setChargeRecordId(wxPrePayReqDto.getChargeRecordId());
        BigDecimal finalAmount = wxPrePayReqDto.getAmount();

        //是否使用优惠券
        if (wxPrePayReqDto.getUserCouponId() != null) {

            Long userCouponId = wxPrePayReqDto.getUserCouponId();

            CommonResult<UserCouponRespDto> userCouponResult = userCouponClient.findById(userCouponId);
            UserCouponRespDto userCouponRespDto = userCouponResult.getData();

            CommonResult<CouponConditionRespDto> couponConditionResult = couponConditionClient.findByCouponId(userCouponRespDto.getCouponId());
            CouponConditionRespDto couponCondition = couponConditionResult.getData();
            Integer condition = couponCondition.getCondition();
            CouponConditionType couponConditionType = CouponConditionType.getCouponConditionTypeByType(couponCondition.getType());
            /*
             * 获取优惠券信息，再判断当前支付金额是否满足 优惠券的满减条件
             */
            if (couponConditionType.getType() == CouponConditionType.REDUCTION.getType()) {
                //是否满足条件，比如满100，减5
                //这里的condition=100，当condition<=finalAmount表示可以使用
                if (new BigDecimal(condition).compareTo(finalAmount) <= 0) {
                    finalAmount = finalAmount.subtract(new BigDecimal(couponCondition.getFaceValue()));
                }
            } else if (couponConditionType.getType() == CouponConditionType.DIRECT_REDUCTION.getType()) {
                //是否满足条件，比如满100，减5
                //当支付金额小于优惠券面值，
                if (finalAmount.compareTo(new BigDecimal(couponCondition.getFaceValue())) <= 0) {
                    finalAmount = BigDecimal.ZERO;
                }
            } else {
                //是否满足条件，比如满100，打1折
                //这里的condition=100，当condition<=finalAmount表示可以使用
                if (new BigDecimal(condition).compareTo(finalAmount) <= 0) {
                    BigDecimal reduce = finalAmount.multiply(new BigDecimal(couponCondition.getFaceValue()));
                    reduce = reduce.divide(new BigDecimal("100.00"), RoundingMode.UP);
                    finalAmount = finalAmount.subtract(reduce);
                }
            }
            chargePayOrder.setUserCouponId(userCouponId);
        }
        chargePayOrder.setAmount(finalAmount);
        chargePayOrder.setPayStatus(WxPayStatusEnum.INIT.getStatus());
        chargePayOrder.setCreateTime(new Date());

        chargePayOrderMapper.insert(chargePayOrder);
        return null;
    }

    /**
     * 这里会涉及到大量的事务，但是我们不能使用 SEATA 来把这个给所有事物都实现，所以，部分功能采用MQ来实现最终一致性
     */
//    @GlobalTransactional
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String payNotify(HttpServletRequest request, HttpServletResponse response) {
        WxPayOrderNotifyResult result;
        try {
            String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
            com.github.binarywang.wxpay.service.WxPayService wxService = new WxPayServiceImpl();
            result = wxService.parseOrderNotifyResult(xmlResult);
        } catch (Exception ex) {
            return getResultMsg("FAILED", "支付回调通知,参数解析失败", response);
        }
        // 结果正确
        String orderNo = result.getOutTradeNo();
        String tradeNo = result.getTransactionId();

        if (StringUtil.isEmpty(orderNo) || StringUtil.isBlank(tradeNo)) {
            return getResultMsg("FAILED", "支付回调通知参数为空", response);
        }

        ChargePayOrder chargePayOrder = chargePayOrderMapper.selectByPrimaryOrder(orderNo);
        if (chargePayOrder == null) {
            log.error("支付回调通知，orderNo={} 不存在", orderNo);
            return getResultMsg("FAILED", "支付回调通知，orderNo=" + orderNo + " 不存在", response);
        }
        chargePayOrder.setUpdateTime(new Date());
        chargePayOrder.setPayOrderNo(tradeNo);
        chargePayOrder.setPayStatus(WxPayStatusEnum.SUCCESS.getStatus());
        //事务--支付单状态变为 支付成功
        chargePayOrderMapper.updateByPrimaryKey(chargePayOrder);

        Long userCouponId = chargePayOrder.getUserCouponId();
        //充电支付回调
        if (userCouponId != null) {
            //事务-----修改这个支付单对应的 充电记录 为已支付
            // TODO: 2024-03-24

            //事务----修改用户积分
            UserPointMessage userPointMessage = new UserPointMessage();
            userPointMessage.setType(UserUpdatePointEnum.ADD.getType());
            userPointMessage.setUserId(1L);
            userPointMessage.setPoint(9);
            userPointMessage.setReqId(MessageReqIdPrefixConstant.USER_POINT_UPDATE_REQ_ID_PREFIX + UUID.randomUUID() + DateUtils.formatDefaultDateMs());
            userPointProducer.sendMessage(JSON.toJSONString(userPointMessage));

            UserCouponUpdateReqDto userCouponUpdateReqDto = new UserCouponUpdateReqDto();
            userCouponUpdateReqDto.setIdList(Collections.singletonList(userCouponId));
            //修改用户优惠券状态为已使用
            userCouponClient.use(userCouponUpdateReqDto);
            return getResultMsg("SUCCESS", "支付回调通知，orderNo=" + orderNo + " 成功", response);
        }
        //第二个事务  修改用于余额+积分
        // TODO: 2024-03-24 完全可以模仿 修改用户积分 方式来做，easy 的很
        return getResultMsg("SUCCESS", "支付回调通知，orderNo=" + orderNo + " 成功", response);
    }

    private String getResultMsg(String returnCode, String returnMsg, HttpServletResponse response) {
        //支付成功，回传通知微信已收到交易成功通知
        HashMap<Object, Object> map = new HashMap<>();
        map.put("return_code", returnCode);
        map.put("return_msg", returnMsg);
        String resultXml = XmlUtil.mapToXml(map, false);
        response.setContentType("text/xml");
        log.info("[支付_微信支付]通知已处理");
        return resultXml;
    }
}
